/**
 * 网页小地图插件
 * anthor: 李青逸(猿某人)
 * create: 2023/5/18
 * how to use:
 * import '···/miniMap.min.js'; Or import { MiniMap } from '···/miniMap.min.js'; Or <script src="https://ymrlqy.top/support/miniMap/miniMap.min.js"></script>
 *  const miniMap = new MiniMap({
        size: 400, // 地图大小-宽度
        borderColor: '#4FC07F', //预览框颜色
        offset: [10, 10], // 距离顶部和右侧分别多少距离
        extraStyle: 'border-radius: 8px;', // 额外的自定义样式
        renderDom: query.isRenderDom ? document.getElementsByClassName('body')[0] : false
    });
    miniMap.init();
    // 移除小地图
    miniMap.remove();
    // 显示、重新渲染小地图
    miniMap.reRender();
 * TODO:
 * 1.页面元素变更后实时刷新？emmm...这个功能有点难搞
 */
class MiniMap {
    static defaultOptions = {
        size: 300, // 地图大小-宽度
        borderColor: "#328756", //预览框颜色
        offset: [10, 10], // 距离顶部和右侧分别多少距离
        extraStyle: "border-radius: 8px;", // 额外的自定义样式
        removeScript: true, // 渲染为dom时是否移除script标签
        renderDom: false //小地图是否渲染为dom元素，可以指定渲染元素，false代表渲染为canvas，为true则尝试渲染id为app的元素副本，为element-node时则渲染指定元素副本（如：document.getElementsByClassName('body')[0]）
    };
    static _t;
    bodyScrollWidth;
    bodyScrollHeight;
    bodyClientWidth;
    bodyClientHeight;
    big_width;
    big_height;
    small_width;
    small_height;
    small_top;
    small_left;
    configOpt;
    proportion;//缩小级别
    scalaLevel;//缩放比例
    doms = {
        miniMapBox: null,
        miniMapCanvas: null,
        miniMapReview: null,
    };
    constructor(option) {
        MiniMap._t = this;
        this.configOpt = {
            ...MiniMap.defaultOptions,
            ...option,
        };
        // this.createDom();
    }
    createDom(isAgain, options = {}) {
        const _t = MiniMap._t;
        _t.configOpt = {
            ..._t.configOpt,
            ...options
        };
        // 创建canvas元素并使用html2canvas将页面缩略图绘制到画布上
        if (isAgain) {
            _t.doms.miniMapReview.style = `position: absolute;z-index: 100;width: ${_t.small_width}px;height: ${_t.small_height}px;top: ${_t.small_top}px;left: ${_t.small_left}px;box-sizing: content-box;box-shadow: ${_t.configOpt.borderColor} 0px 0px 0px 2px inset;${_t.configOpt.extraStyle}`;
        } else {
            _t.remove();
            if (document.getElementById("miniMapBox"))
                document.getElementById("miniMapBox").remove();
            const drawPageOnCanvas = () => {
                _t.doms.miniMapBox = document.createElement("div");
                _t.doms.miniMapBox.id = "miniMapBox";
                _t.doms.miniMapBox.style = `position: fixed;width: ${
                    _t.big_width
                }px;height: ${_t.big_height}px;top: ${
                    _t.configOpt.offset[0]
                }px;right: ${
                    _t.configOpt.offset[1] || _t.configOpt.offset[0]
                }px;z-index: 999999999;overflow: hidden;border-radius: 8px;box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;`;
                _t.doms.miniMapCanvas = document.createElement(_t.configOpt.renderDom ? 'div' : "canvas");
                _t.doms.miniMapCanvas.id = "miniMapCanvas";
                _t.doms.miniMapCanvas.style.position = 'relative';
                _t.doms.miniMapCanvas.style.display = 'block';
                _t.doms.miniMapCanvas.style['transform-origin'] = `0 0`;
                if (_t.configOpt.renderDom) {
                    _t.doms.miniMapCanvas.style.width = _t.bodyScrollWidth + 'px';
                    _t.doms.miniMapCanvas.style.height = _t.bodyScrollHeight + 'px';
                    _t.doms.miniMapCanvas.style.transform = `scale(${_t.scalaLevel})`;
                } else {
                    _t.doms.miniMapCanvas.width = _t.big_width;
                    _t.doms.miniMapCanvas.height = _t.big_height;
                }
                _t.doms.miniMapReview = document.createElement("div");
                _t.doms.miniMapReview.id = "miniMapReview";
                _t.doms.miniMapReview.style = `position: absolute;z-index: 100;width: ${_t.small_width+1}px;height: ${_t.small_height}px;top: ${_t.small_top}px;left: ${_t.small_left}px;box-sizing: content-box;box-shadow: ${_t.configOpt.borderColor} 0px 0px 0px 2px inset;${_t.configOpt.extraStyle}`;
                if (_t.configOpt.renderDom) {
                    // 1.是否渲染为dom
                    // 2.判断是否指定dom内容副本，若未指定，则尝试取#app元素，取不到则取整个body的副本
                    const copyBodyDom = (typeof _t.configOpt.renderDom === 'boolean' ? (document.getElementById('app') || document.body) : _t.configOpt.renderDom).cloneNode(true); //创建副本
                    _t.configOpt.removeScript && _t._deleteNodeFromDom(copyBodyDom, copyBodyDom.getElementsByTagName('script')); // 删除所有script标签
                    _t.doms.miniMapCanvas.appendChild(copyBodyDom);
                    _t.doms.miniMapBox.appendChild(_t.doms.miniMapCanvas);
                    _t.doms.miniMapBox.appendChild(_t.doms.miniMapReview);
                    document.body.appendChild(_t.doms.miniMapBox);
                    _t._addEvent();
                } else {
                    // 渲染为canvas图片
                    html2canvas(document.body, {
                        scale: 1,
                        allowTaint: true,
                        useCORS: true, //跨域使用
                    }).then((saveCanvas) => {
                        _t.doms.miniMapCanvas
                            .getContext("2d")
                            .drawImage(
                                saveCanvas,
                                0,
                                0,
                                _t.big_width,
                                _t.big_height
                            );
                        _t.doms.miniMapBox.appendChild(_t.doms.miniMapCanvas);
                        _t.doms.miniMapBox.appendChild(_t.doms.miniMapReview);
                        document.body.appendChild(_t.doms.miniMapBox);
                        _t._addEvent();
                    });
                }

            };
            if (!window.html2canvas && !_t.configOpt.renderDom) {
                _t._loadAsyncScript('https://cdn.bootcdn.net/ajax/libs/html2canvas/1.4.1/html2canvas.min.js', () => {
                    drawPageOnCanvas();
                });
                // import("./html2canvas.min.js").then((module) => {
                //     drawPageOnCanvas();
                // });
            } else {
                drawPageOnCanvas();
            }
        }
    }
    remove() {
        this.doms.miniMapReview && this.doms.miniMapReview.remove();
        this.doms.miniMapCanvas && this.doms.miniMapCanvas.remove();
        this.doms.miniMapBox && this.doms.miniMapBox.remove();
        this.doms.miniMapReview = null;
        this.doms.miniMapCanvas = null;
        this.doms.miniMapBox = null;
        window.onscroll = null;
        window.removeEventListener("resize", this._resize);
    }
    reRender(options) {
        this._resize(false, options);
    }
    init() {
        window.addEventListener("resize", this._resize);
        this._resize(false);
    }
    _addEvent() {
        const _t = MiniMap._t;
        _t.doms.miniMapReview.onmousedown = () => {
            // console.log('onmousedown', e.layerX, e.layerY);
            _t.doms.miniMapReview.onmousemove = (e) => {
                const x = e.clientX - _t.doms.miniMapBox.offsetLeft;
                const y = e.clientY - _t.doms.miniMapBox.offsetTop;
                const halfWidth = _t.small_width / 2;
                const halfHeight = _t.small_height / 2;
                const left =
                    x - halfWidth < 0
                        ? 0
                        : x + halfWidth > _t.big_width
                        ? _t.big_width - _t.small_width
                        : x - halfWidth;
                const top =
                    y - halfHeight < 0
                        ? 0
                        : y + halfHeight > _t.big_height
                        ? _t.big_height - _t.small_height
                        : y - halfHeight;
                _t.doms.miniMapReview.style.top = top + "px";
                _t.doms.miniMapReview.style.left = left + "px";
                (document.documentElement || document.body).scrollTop =
                    top * _t.proportion;
                (document.documentElement || document.body).scrollLeft =
                    left * _t.proportion;
            };
        };
        _t.doms.miniMapReview.onmouseup = () => {
            _t.doms.miniMapReview.onmousemove = null;
        };
        // _t.doms.miniMapBox.onmouseout = () => {
        //     _t.doms.miniMapReview.onmousemove = null;
        // };
        window.onscroll = () => {
            const scrollTop = (document.documentElement || document.body).scrollTop;
            const scrollLeft = (document.documentElement || document.body).scrollLeft;
            _t.doms.miniMapReview.style.top = (scrollTop / _t.proportion) + "px";
            _t.doms.miniMapReview.style.left = (scrollLeft / _t.proportion) + "px";
        };
    }
    _resize(isAgain, options) {
        const _t = MiniMap._t;
        _t.bodyScrollWidth = (document.documentElement || document.body).scrollWidth;
        _t.bodyScrollHeight = (document.documentElement || document.body).scrollHeight;
        _t.bodyClientWidth = window.innerWidth;
        _t.bodyClientHeight = window.innerHeight;
        _t.proportion = _t.bodyScrollWidth / _t.configOpt.size;
        _t.big_width = _t.configOpt.size;
        _t.big_height = _t.bodyScrollHeight / _t.proportion;
        _t.small_width =
            (_t.bodyClientWidth / _t.bodyScrollWidth) * _t.big_width;
        _t.small_height =
            (_t.bodyClientHeight / _t.bodyScrollHeight) * _t.big_height;
        _t.small_top = (document.documentElement || document.body).scrollTop / _t.proportion;
        _t.small_left = (document.documentElement || document.body).scrollLeft / _t.proportion;
        _t.scalaLevel = _t.big_width / _t.bodyScrollWidth;
        // console.log(`bodyScrollWidth: ${_t.bodyScrollWidth}\nbodyScrollHeight: ${_t.bodyScrollHeight}\nbodyClientWidth: ${_t.bodyClientWidth}\nbodyClientHeight: ${_t.bodyClientHeight}\nproportion: ${_t.proportion}\nbig_width: ${_t.big_width}\nbig_height: ${_t.big_height}\nsmall_width: ${_t.small_width}\nsmall_height: ${_t.small_height}\nsmall_top: ${_t.small_top}\nsmall_left: ${_t.small_left}`);
        _t.createDom(!!isAgain, options);
    }
    _loadAsyncScript(url, callback) {
        const script = document.createElement("script");
        script.type = "text/javascript";
        if (script.readyState) {
            // 兼容IE浏览器
            script.onreadystatechange = function () {
                if (
                    script.readyState === "complete" ||
                    script.readyState === "loaded"
                ) {
                    callback();
                }
            };
        } else {
            // Chrome, Safari, FireFox, Opera可执行
            script.onload = function () {
                callback();
            };
        }
        script.src = url; //将src属性放在后面，保证监听函数能够起作用
        document.head.appendChild(script);
    }
    _deleteNodeFromDom(dom = document.body, node) {
        // console.log('需要删除的元素', node);
        if (typeof node === 'string') {
            dom.removeChild(node)
        } else {
            while (node.length) {
                dom.removeChild(node[0]);
            }
        }
    }
}
(() => {
    "object" == typeof exports && "object" == typeof module
        ? (module.exports = MiniMap)
        : "function" == typeof define && define.amd
        ? define([], MiniMap)
        : "object" == typeof exports
        ? (exports.default = MiniMap)
        : (window.MiniMap = MiniMap);
})();
